//Page 86.

#include <iostream.h>
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <semaphore.h>
	  
class Job
{
public:
	int id;
	Job * pNext;
	Job(int ID):id(ID){}
	inline void PrintJobID()
	{
		cout<< "This JobID = "<< id << endl;
	}
	~Job()
	{
		cout<< "~~Job" << endl;
	}
};

Job * pRemainJobFirst = NULL;
pthread_mutex_t pRemainJobFirst_mutex = PTHREAD_MUTEX_INITIALIZER;
sem_t job_queue_count;
pthread_mutex_t job_queue_count_mutex = PTHREAD_MUTEX_INITIALIZER;

void * thread_function ( void * arg )
{
	static int num = 0;
	num++;
	int thisNum = num;
	cout<< "Inside thread: " << num << endl;
	

	while ( 1 )
	{
//		pthread_mutex_lock ( &job_queue_count_mutex );
		sem_wait (&job_queue_count);
//		pthread_mutex_unlock ( &job_queue_count_mutex );

		pthread_mutex_lock ( &pRemainJobFirst_mutex );
		cout<< "Thread " << thisNum << " is processing" << endl;
		Job *pNextJob = pRemainJobFirst;
		pRemainJobFirst = pRemainJobFirst->pNext;
		pthread_mutex_unlock ( &pRemainJobFirst_mutex );	
		pNextJob->PrintJobID();
		delete pNextJob;
		sleep(5);
	}
	cout<< "Thread " << thisNum << " is exiting" << endl;
	return NULL;
}

void * produce_job ( void * arg )
{
	int i = 0;

	while(1)
	{
		i++;
		Job * pTmpJob = new Job(i);
		
		pthread_mutex_lock ( &pRemainJobFirst_mutex );
		pTmpJob->pNext = pRemainJobFirst;
		pRemainJobFirst = pTmpJob;
		pthread_mutex_unlock ( &pRemainJobFirst_mutex );

		cout<< "Produce Job " << i << endl;
		sem_post(&job_queue_count);
		sleep(2);
	}
	
	
	return NULL;
}	

int main()
{
	pRemainJobFirst = new Job(0);
	pRemainJobFirst->pNext = NULL;
	
	const int count = 3;
	pthread_t tid[count+1];

	sem_init(&job_queue_count, 0, 1);

	pthread_create(&(tid[0]), NULL, produce_job, NULL);
	
	for(int i=1; i <= count; i++)
		pthread_create(&(tid[i]), NULL, thread_function, NULL);
	
	for(int i=0; i <= count; i++)
		pthread_join(tid[i], NULL);

	return 0;
}
